// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System.Collections.Generic;
using System.Linq;
using SnapObjects.Commons;

namespace SnapObjects.Data
{
    internal class ModelDeleter<TModel>
    {
        private readonly DataContext _context;
        private readonly IMapperContext _mapperContext;
        private readonly IMapperChangeTracker _changeTracker;

        public ModelDeleter(
            DataContext context,
            IMapperContext mapperContext,
            IMapperChangeTracker changeTracker)
        {
            _context = context;
            _mapperContext = mapperContext;
            _changeTracker = changeTracker;
        }

        public void TrackDeleteByKey(params object[] parameters)
        {
            var modelLoadable = new ModelLoadable<TModel>(_context, new ModelSelectBuilder(_context));
            var master = modelLoadable.LoadByKey(parameters)
                                        .FirstOrDefault();

            this.TrackDeleteEmbedded(master, modelLoadable.BoundParameters);

            var masterDeleteBuilder = new ModelDeleteBuilder(_context, typeof(TModel));

            _changeTracker.TrackSqlCUD(_context,
                masterDeleteBuilder.GetDeleteByKeyBuilder(), parameters);
        }

        private void TrackDeleteEmbedded(object model, IReadOnlyList<IAdoDbParameter> parentParameters)
        {
            if (model == null)
            {
                return;
            }

            var modelMeta = ModelMetaManager.GetModelMeta(model.GetType());

            foreach (var property in modelMeta.CascadeDeleteProperties)
            {
                var modelEmbedded = property.ModelEmbedded;
                var embeddedMeta = ModelMetaManager.GetModelMeta(modelEmbedded.ModelType);
                var embeddedLoader = new EmbeddedLoader(_context, new ModelSelectBuilder(_context));

                if (embeddedMeta.HasCascadeDeleteProperties)
                {
                    var isModel = property.EmbeddedType.IsModel();
                    if (!isModel)
                    {
                        continue;
                    }

                    var result = embeddedLoader.LoadEmbedded(model, property.Name, parentParameters);
                    if (result == null)
                    {
                        continue;
                    }

                    if (embeddedLoader.IsResultCollection)
                    {
                        var collection = (IEnumerable<object>)result;
                        foreach (var item in collection)
                        {
                            this.TrackDeleteEmbedded(item, embeddedLoader.BoundParameters);
                        }
                    }
                    else
                    {
                        this.TrackDeleteEmbedded(result, embeddedLoader.BoundParameters);
                    }
                }

                var definedParameters = embeddedLoader.GetSqlAndDefinedParameters(
                                            property,
                                            out var sqlText);

                var embeddedParams = embeddedLoader.BindParameterValues(
                                            model,
                                            modelEmbedded.ParamValue,
                                            definedParameters,
                                            parentParameters);

                var deleteBuilder = new ModelDeleteBuilder(_context, modelEmbedded.ModelType);

                if (modelEmbedded.QuerySelector == QuerySelector.SelectByKey)
                {
                    _changeTracker.TrackSqlCUD(_context,
                        deleteBuilder.GetDeleteByKeyBuilder(), this.GetParameterValues(embeddedParams));
                }
                else if (modelEmbedded.QuerySelector == QuerySelector.Select)
                {
                    _changeTracker.TrackSqlCUD(_context,
                        deleteBuilder.GetDeleteBuilder(), this.GetParameterValues(embeddedParams));
                }
            }
        }

        private object[] GetParameterValues(IAdoDbParameter[] sqlParams)
        {
            return sqlParams.Select(o => o.Value).ToArray();
        }
    }
}
